<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8"/>
    <title>本地 AI - 多会话</title>
    <meta name="viewport" content="width=device-width,initial-scale=1"/>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        /* 滚动条样式优化 */
        ::-webkit-scrollbar {
            width: 6px;
            height: 6px;
        }

        ::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.05);
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb {
            background: #ffffff33;
            border-radius: 3px;
            transition: background 0.2s;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: #ffffff50;
        }

        /* 平滑滚动 */
        html {
            scroll-behavior: smooth;
        }

        /* 输入框自动调整高度 */
        #inp {
            min-height: 42px;
            max-height: 120px;
        }

        /* 消息出现动画 */
        .message-enter {
            animation: fadeIn 0.3s ease-out forwards;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
    </style>
</head>

<body class="bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 h-screen flex text-sm text-slate-200">
<!-- 左侧栏 -->
<aside id="sidebar" class="w-60 bg-slate-800/40 backdrop-blur-md border-r border-slate-700/50 flex flex-col transition-all duration-300 ease-in-out">
    <div class="p-4 border-b border-slate-700/50 flex items-center gap-2">
        <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-400 flex items-center justify-center">
            <i class="fa fa-brain text-white"></i>
        </div>
        <h1 class="font-bold text-white text-lg">本地 AI</h1>
    </div>

    <!-- 模型下拉 -->
    <div class="p-3">
        <select id="modelSel"
                class="w-full bg-slate-700/30 border border-slate-600 rounded-lg px-3 py-2 text-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500/50 transition-all">
            <option disabled>加载模型…</option>
        </select>
    </div>

    <!-- 会话列表 -->
    <div id="sessionList" class="flex-1 p-2 space-y-1 overflow-y-auto scrollbar-thin"></div>

    <!-- 新建会话 -->
    <div class="p-3 border-t border-slate-700/50">
        <button onclick="newSession()"
                class="w-full text-slate-300 hover:text-white bg-slate-700/30 hover:bg-blue-600/20 hover:border-blue-500/50 border border-transparent rounded-lg px-4 py-2.5 text-sm transition-all flex items-center justify-center gap-2 group">
            <i class="fa fa-plus-circle group-hover:scale-110 transition-transform"></i>
            <span>新建会话</span>
        </button>
    </div>
</aside>

<!-- 主区域 -->
<main class="flex-1 flex flex-col">
    <header class="h-14 flex items-center px-4 border-b border-slate-700/50 bg-slate-800/20 backdrop-blur-sm">
        <button onclick="toggleSidebar()"
                class="md:hidden text-slate-300 hover:text-white mr-3 p-1.5 rounded-full hover:bg-slate-700/30 transition-all">
            <i class="fa fa-bars"></i>
        </button>
        <span id="chatTitle" class="font-medium text-white">新的对话</span>
        <div class="ml-auto flex items-center gap-2">
            <span id="modelIndicator" class="text-xs text-slate-400 bg-slate-700/30 px-2 py-1 rounded-full">
                <i class="fa fa-microchip mr-1"></i>加载中
            </span>
        </div>
    </header>

    <!-- 消息区 -->
    <div id="box" class="flex-1 overflow-y-auto px-4 py-6 space-y-6">
        <!-- 欢迎消息 -->
        <div class="flex justify-center message-enter">
            <div class="max-w-xl text-center bg-slate-800/30 rounded-2xl px-6 py-4 text-slate-300">
                <div class="w-12 h-12 rounded-full bg-gradient-to-br from-blue-500 to-cyan-400 mx-auto mb-3 flex items-center justify-center">
                    <i class="fa fa-robot text-white text-xl"></i>
                </div>
                <h3 class="font-medium text-white mb-2">欢迎使用本地 AI</h3>
                <p class="text-sm text-slate-400">请选择一个模型并开始对话，所有数据将保存在本地</p>
            </div>
        </div>
    </div>

    <!-- 输入区 -->
    <div class="p-4 border-t border-slate-700/50 bg-slate-800/20 backdrop-blur-sm">
        <form onsubmit="send();return false" class="flex gap-3">
            <textarea id="inp"
                      rows="1"
                      placeholder="输入消息... (Shift+Enter换行)"
                      class="flex-1 resize-none rounded-xl border border-slate-600 bg-slate-700/40 px-4 py-2.5 text-slate-200 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500/50 focus:border-transparent transition-all"></textarea>
            <button type="submit"
                    class="bg-blue-600 hover:bg-blue-500 text-white rounded-xl px-4 py-2.5 transition-all transform hover:scale-105 active:scale-95">
                <i class="fa fa-paper-plane"></i>
            </button>
        </form>
        <div class="text-xs text-slate-500 mt-2 ml-4">
            <i class="fa fa-info-circle mr-1"></i>所有对话仅存储在本地浏览器中
        </div>
    </div>
</main>

<script>
    const API = 'http://localhost:11434';
    const box = document.getElementById('box');
    const inp = document.getElementById('inp');
    const sel = document.getElementById('modelSel');
    const sessionList = document.getElementById('sessionList');
    const chatTitle = document.getElementById('chatTitle');
    const modelIndicator = document.getElementById('modelIndicator');

    let sessions = [];
    let currentId = null;

    /* ---------- 初始化 ---------- */
    loadModels();
    loadStorage();

    // 自动调整输入框高度
    inp.addEventListener('input', function () {
        this.style.height = 'auto';
        this.style.height = (this.scrollHeight) + 'px';
    });

    /* ---------- 模型列表 ---------- */
    async function loadModels() {
        try {
            const res = await fetch(`${API}/api/tags`);
            const data = await res.json();
            sel.innerHTML = '';
            data.models.forEach(m => {
                const o = document.createElement('option');
                o.value = o.textContent = m.name;
                sel.appendChild(o);
            });
            // 更新模型指示器
            modelIndicator.innerHTML = `<i class="fa fa-microchip mr-1"></i>${sel.value}`;
            sel.addEventListener('change', () => {
                modelIndicator.innerHTML = `<i class="fa fa-microchip mr-1"></i>${sel.value}`;
            });
        } catch {
            sel.innerHTML = '<option>模型加载失败</option>';
            modelIndicator.innerHTML = `<i class="fa fa-exclamation-triangle mr-1 text-amber-500"></i>连接失败`;
        }
    }

    /* ---------- 会话操作 ---------- */
    function newSession() {
        const id = Date.now();
        sessions.unshift({id, title: '新的对话', messages: []});
        saveStorage();
        renderSessions();
        switchSession(id);
    }

    function switchSession(id) {
        currentId = id;
        const s = sessions.find(s => s.id === id);
        chatTitle.textContent = s.title;
        renderMessages(s.messages);
        // 滚动到底部
        box.scrollTop = box.scrollHeight;
    }

    function renderSessions() {
        sessionList.innerHTML = '';
        sessions.forEach(s => {
            const div = document.createElement('div');
            div.className = 'px-3 py-2.5 rounded-lg hover:bg-slate-700/30 cursor-pointer truncate transition-all text-sm flex items-center gap-2 group';

            // 确定图标和颜色
            let iconClass = 'fa-comment-o text-slate-400 group-hover:text-blue-400 transition-colors';
            if (s.id === currentId) {
                iconClass = 'fa-comment text-blue-400';
                div.classList.add('bg-blue-600/20', 'text-blue-300');
            }

            div.innerHTML = `
                <i class="fa ${iconClass}"></i>
                <span>${s.title}</span>
                <button onclick="deleteSession(${s.id})" class="ml-auto text-slate-400 hover:text-red-400 p-1 transition-colors">
                    <i class="fa fa-times"></i>
                </button>
            `;
            div.onclick = () => switchSession(s.id);
            sessionList.appendChild(div);
        });
    }

    function deleteSession(id) {
        // 确认删除
        if (confirm('确定要删除这个会话吗？')) {
            // 从会话列表中移除
            sessions = sessions.filter(s => s.id !== id);
            
            // 如果删除的是当前会话，切换到第一个会话
            if (id === currentId) {
                currentId = sessions.length > 0 ? sessions[0].id : null;
            }
            
            saveStorage();
            renderSessions();
            
            // 如果还有会话，切换到第一个
            if (sessions.length > 0) {
                switchSession(currentId);
            } else {
                // 如果没有会话了，创建新会话
                newSession();
            }
        }
    }

    function renderMessages(msgs) {
        // 清空消息区（保留欢迎消息如果没有对话）
        box.innerHTML = '';
        if (msgs.length === 0) {
            box.innerHTML = `
                <div class="flex justify-center message-enter">
                    <div class="max-w-xl text-center bg-slate-800/30 rounded-2xl px-6 py-4 text-slate-300">
                        <div class="w-12 h-12 rounded-full bg-gradient-to-br from-blue-500 to-cyan-400 mx-auto mb-3 flex items-center justify-center">
                            <i class="fa fa-robot text-white text-xl"></i>
                        </div>
                        <h3 class="font-medium text-white mb-2">欢迎使用本地 AI</h3>
                        <p class="text-sm text-slate-400">请选择一个模型并开始对话，所有数据将保存在本地</p>
                    </div>
                </div>
            `;
            return;
        }

        msgs.forEach(m => addBubbleDOM(m.text, m.role));
        box.scrollTop = box.scrollHeight;
    }

    /* ---------- 消息 ---------- */
    function send() {
        const text = inp.value.trim();
        if (!text || !sel.value) return;
        const model = sel.value;

        const session = sessions.find(s => s.id === currentId);
        // 更新会话标题（首次消息）
        if (session.messages.length === 0) {
            session.title = text.length > 20 ? text.substring(0, 20) + '...' : text;
        }
        session.messages.push({role: 'user', text});
        addBubbleDOM(text, 'user');

        // 清空输入框并重置高度
        inp.value = '';
        inp.style.height = 'auto';
        saveStorage();
        renderSessions();

        // 显示加载状态
        const loadingDiv = document.createElement('div');
        loadingDiv.className = 'flex justify-start message-enter';
        loadingDiv.innerHTML = `
            <div class="max-w-xl rounded-2xl px-4 py-3 bg-slate-800/50 text-slate-200">
                <div class="flex items-center gap-2">
                    <div class="w-6 h-6 rounded-full bg-gradient-to-br from-blue-500 to-cyan-400 flex items-center justify-center text-xs text-white">AI</div>
                    <div class="flex gap-1">
                        <div class="w-2 h-2 rounded-full bg-slate-400 animate-bounce [animation-delay:-0.3s]"></div>
                        <div class="w-2 h-2 rounded-full bg-slate-400 animate-bounce [animation-delay:-0.15s]"></div>
                        <div class="w-2 h-2 rounded-full bg-slate-400 animate-bounce"></div>
                    </div>
                </div>
            </div>`;
        box.appendChild(loadingDiv);
        box.scrollTop = box.scrollHeight;

        fetch(`${API}/api/generate`, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({model, prompt: text, stream: false})
        })
            .then(r => r.json())
            .then(data => {
                // 移除加载状态
                box.removeChild(loadingDiv);

                const reply = data.response.trim();
                session.messages.push({role: 'ai', text: reply});
                addBubbleDOM(reply, 'ai');
                saveStorage();
            })
            .catch(e => {
                // 移除加载状态
                box.removeChild(loadingDiv);

                const err = '❌ 发生错误: ' + e.message;
                session.messages.push({role: 'ai', text: err});
                addBubbleDOM(err, 'ai');
                saveStorage();
            });
    }

    function addBubbleDOM(text, role) {
        const div = document.createElement('div');
        div.className = 'flex message-enter ' + (role === 'user' ? 'justify-end' : 'justify-start');

        let avatar, bubbleClass;
        if (role === 'user') {
            avatar = `<div class="w-8 h-8 rounded-full bg-slate-600 flex items-center justify-center text-white shrink-0">
                        <i class="fa fa-user"></i>
                      </div>`;
            bubbleClass = 'bg-blue-600 text-white';
        } else {
            avatar = `<div class="w-8 h-8 rounded-full bg-gradient-to-br from-blue-500 to-cyan-400 flex items-center justify-center text-white shrink-0">
                        <i class="fa fa-robot"></i>
                      </div>`;
            bubbleClass = 'bg-slate-800/50 text-slate-200';
        }

        // 格式化文本（简单处理换行）
        const formattedText = text.replace(/\n/g, '<br>');

        div.innerHTML = `
            <div class="flex items-start gap-3 max-w-xl">
              ${role === 'ai' ? avatar : ''}
              <div class="rounded-2xl px-4 py-3 ${bubbleClass}">
                ${formattedText}
                <div class="text-xs opacity-60 mt-1 text-right">${new Date().toLocaleTimeString()}</div>
              </div>
              ${role === 'user' ? avatar : ''}
            </div>`;

        box.appendChild(div);
        box.scrollTop = box.scrollHeight;
    }

    /* ---------- 本地存储 ---------- */
    function saveStorage() {
        localStorage.setItem('aiSessions', JSON.stringify(sessions));
        localStorage.setItem('currentId', currentId);
    }

function loadStorage() {
    sessions = JSON.parse(localStorage.getItem('aiSessions') || '[]');
    currentId = Number(localStorage.getItem('currentId'));

    if (!sessions.length) {
        newSession();
    } else {
        if (!sessions.find(s => s.id === currentId)) currentId = sessions[0].id;
        renderSessions();
        switchSession(currentId);
    }
}

/* ---------- 侧边栏折叠（手机） ---------- */
function toggleSidebar() {
    const sidebar = document.getElementById('sidebar');
    sidebar.classList.toggle('-translate-x-full');
}

// 点击消息区域自动聚焦输入框（提升用户体验）
box.addEventListener('click', () => {
    inp.focus();
});


inp.addEventListener('keydown', function (e) {
    if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        send();
    }
});
</script>
</body>
</html>